package cc.verywell.pureblock.util;

import java.io.Closeable;
import java.io.IOException;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicBoolean;

import cc.verywell.pureblock.log.Pblog;
import cc.verywell.pureblock.log.ThreadListener;

/**
* 
* Copyright © 2019 TanYaqiu. All rights reserved.
* @author TanYaqiu 
* @address flat 601
* @date 2019年6月19日 上午10:51:21
*/
public class AutoClosed implements Runnable,Closeable {

	final private AtomicBoolean closed = new AtomicBoolean(false);
	final private ReferenceQueue<Object> que = new ReferenceQueue<Object>();
	final private ConcurrentHashMap<CloseReference, AutoClosed> strong = new ConcurrentHashMap<>();
	final private ExecutorService exe;
	final private ThreadListener tl;
	public AutoClosed(ThreadGroup tg,ThreadListener tl,int closeThreads) {
		this.tl = tl!=null?tl:ThreadListener.NONE;
		exe = Executors.newFixedThreadPool(closeThreads, new ThreadFactory() {
			@Override
			public Thread newThread(Runnable r) {
				return new Thread(tg, r, "AutoClosed");
			}
		});
		while(closeThreads-->0) {
			exe.execute(this);
		}
	}
	@Override
	public void close(){
		if(!closed.getAndSet(true)) {
			exe.shutdownNow();
			for (CloseReference r : strong.keySet()) {
				try {
					r.run.run();
				}catch(Throwable e) {
	            	if(Pblog.INIT)Pblog.INIT(e);
				}
			}
		}
	}

	
	public void add(Object o,Runnable c) {
		if(closed.get()) {
			c.run();
			return;
		}
		strong.put(new CloseReference(o,que,c),this);
	}
	
	static public class CloseReference extends PhantomReference<Object>{

		final public Runnable run;
		public CloseReference(Object referent, ReferenceQueue<? super Object> q, Runnable run) {
			super(referent, q);
			this.run = run;
		}
		
	}
	
	@Override
	public void run() {
		tl.threadIn();
		while (!Thread.currentThread().isInterrupted() && !closed.get()) {
            // get the next reference to cleanup
            try {
            	tl.threadWait();
            	CloseReference r = (CloseReference) que.remove();
            	tl.threadWake();
            	strong.remove(r);
            	r.run.run();
            }
            catch (InterruptedException e) {
               tl.threadOut();
               Thread.currentThread().interrupt();
               return;
            }
            catch (Throwable e) {
            	if(Pblog.INIT)Pblog.INIT(e);
            }
        }
		tl.threadOut();
		if(!closed.get()) {
			exe.submit(this);
		}
	}
	
	

}
